﻿using M3U.NET;
using org.nativeinstruments.nml;
using ShellProgressBar;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TextProcessingLibrary;

namespace NmlPlaylistExporter
{
    class PlaylistExporter
    {
        class TrackLookup
        {
            private readonly List<COLLECTIONENTRY> _collection;


            public TrackLookup(List<COLLECTIONENTRY> collection)
            {
                _collection = collection;
            }


            public COLLECTIONENTRY this[string location]
            {
                get { return _collection.FirstOrDefault(e => getFullPath(e.LOCATION) == location); }
            }


            private string getFullPath(LOCATION location)
            {
                return string.Format("{0}{1}{2}", location.VOLUME, location.DIR, location.FILE);
            }
        }


        class XmlSerializer : ISerializer
        {
            public Stream Serialize<T>(T item)
            {
                throw new NotImplementedException();
            }

            public T Deserialize<T>(Stream stream)
            {
                var serializer = new System.Xml.Serialization.XmlSerializer(typeof(NML));
                return (T)serializer.Deserialize(stream);
            }
        }



        private readonly IEnumerable<ProcessingTask> _tasks;
        private TrackLookup _trackLookup;
        private ProgressBar _progressBar;


        public PlaylistExporter(IEnumerable<ProcessingTask> tasks)
        {
            _tasks = tasks;
        }


        public void Export(string pathToNml, string outputDir)
        {
            var nml = parseNml(pathToNml);
            Console.WriteLine();

            _trackLookup = new TrackLookup(nml.COLLECTION.COLLECTIONENTRY);

            var root = nml.PLAYLISTS.NODE.FirstOrDefault();
            exportPlaylists(outputDir, root);
        }


        private NML parseNml(string path)
        {
            return TextHelper.DeserializeWithProgress<NML>(path, new XmlSerializer());
        }

        private void exportPlaylists(string outputDir, NODE node)
        {
            if (node.TYPE == "PLAYLIST")
            {
                string file = playlistToM3u(node.NAME, node.PLAYLIST, outputDir);
                Console.WriteLine("Created playlist: " + file);
                Console.WriteLine();
            }
            else if (node.SUBNODES != null && node.SUBNODES.COUNT > 0)
            {
                if (node.NAME != "$ROOT")
                {
                    outputDir = Path.Combine(outputDir, makeValidFolderName(node.NAME));
                    if (!Directory.Exists(outputDir))
                        Directory.CreateDirectory(outputDir);
                }

                foreach (var subNode in node.SUBNODES.NODES)
                    exportPlaylists(outputDir, subNode);
            }
        }

        private string playlistToM3u(string name, PLAYLIST playlist, string outputDir)
        {
            string outputFile = Path.Combine(outputDir, string.Format("{0}.m3u", makeValidFileName(name)));

            _progressBar = new ProgressBar(playlist.ENTRIES, "Create playlist " + outputFile, ConsoleColor.Cyan);

            if (File.Exists(outputFile))
                File.Delete(outputFile);

            var result = new M3UFile(new FileInfo(outputFile));

            string location;
            COLLECTIONENTRY entry;
            for (int i = 0; i < playlist.ENTRIES; i++)
            {
                location = playlist.PLAYLISTENTRY[i].PRIMARYKEY.KEY;
                entry = _trackLookup[location];

                if (entry == null)
                {
                    _progressBar.Tick("Skipped file: " + location);
                    continue;
                }

                if (_tasks != null)
                {
                    foreach (var task in _tasks)
                        task.Execute(ref location);
                }

                _progressBar.Tick("Executed tasks on " + location);

                result.Files.Add(new MediaItem
                {
                    Runtime = entry.INFO.PLAYTIME,
                    Inf = string.Format("{0} - {1}", entry.ARTIST, entry.TITLE),
                    Location = location
                });
            }

            result.Save();
            
            Console.WriteLine();
            Console.WriteLine();
            
            return outputFile;
        }

        private string makeValidFolderName(string name)
        {
            foreach (char c in Path.GetInvalidPathChars())
                name = name.Replace(System.Char.ToString(c), "");
            return name;
        }

        private string makeValidFileName(string name)
        {
            foreach (char c in Path.GetInvalidFileNameChars())
                name = name.Replace(System.Char.ToString(c), "");
            return name;
        }
    }
}
